using AnimCollections;
using AOT;
using Unity.Burst;
using UnityEngine.Animations;
using UnityEngine.Playables;

namespace DA.AnimGraph {
    [BurstCompile]
    public unsafe partial struct ClipNode : INode {
        public struct SetupData : INodeSetupData {
            public ClipLinkDesc clipDesc;
            public FloatLinkDesc playbackSpeedDesc;
        }

        /*
         * 数据初始化可以分4个阶段
         * - SetupLink
         * 关联 NodeLink（已移除）
         * 从配置构建节点数据
         * 以上两者在实例化节点时立即执行
         * - Init
         * - BuildPlayableGraph
         * 以上两者都是在加入 AnimGraph，并且加入 AnimGraphController 之后，由 AnimGraphController 调用
         * 区别在于参数不同
         * Init 目的在于收集节点信息，比如 slot 节点
         * BuildPlayableGraph 则是做最后的初始化，比如 playable
         */

        // Setup 中需要初始化
        // NativeArray 这种需要在 buffer 中申请空间的，只能在 Setup 中申请，但是初始化内容可以滞后
        private ClipLink linkClip;
        private FloatLink playbackSpeed;

        // BuildPlayableGraph 中需要初始化
        private AnimationClipPlayable playable;
        private TimeState timeState;


        private void Setup(in SetupData data, ref GraphBuffer buffer, in NodeLink* link) {
            linkClip = new ClipLink(data.clipDesc);
            playbackSpeed = new FloatLink(data.playbackSpeedDesc);
        }

        private static Playable BuildPlayableGraph(in void* node, in NodeLink* link, in PlayableGraph graph, in BuildContext buildContext) {
            var self = (ClipNode*)node;

            var animClip = self->linkClip.Get(buildContext);
            self->timeState = new(TimeState.AccumulateMode.Time, animClip?.isLooping ?? false);
            link->SetTimeState(self->timeState);

            self->timeState.SetDuration(self->linkClip.duration);
            self->playable = AnimationClipPlayable.Create(graph, animClip);
            self->playable.SetApplyFootIK(false);
            return self->playable;
        }

        [BurstCompile]
        [MonoPInvokeCallback(typeof(NodeLink.EvaluateDelegate))]
        private static void Evaluate(in void* node, bool onEnter, in EvaluateContext evaluateContext, in GraphContext graphContext, ref UpstreamData upstream, ref ScopeContext scope) {
            var self = (ClipNode*)node;

            var playbackSpeed = self->playbackSpeed.Evaluate(onEnter, evaluateContext, graphContext, scope);
            self->timeState.Accumulate(evaluateContext.deltaTime * playbackSpeed);
        }

        [BurstCompile]
        [MonoPInvokeCallback(typeof(NodeLink.ApplyDelegate))]
        private static void Apply(in void* node) {
            var self = (ClipNode*)node;

            float time = self->timeState.time;
            self->playable.SetTime(time);
        }
    }
}
